#include <stdlib.h>
#include <iostream>
#include "Arbre.h"
#include "Symbole.h"
#include "SymboleValue.h"

////////////////////////////////////////////////////////////////////////////////
// NoeudSeqInst
////////////////////////////////////////////////////////////////////////////////

NoeudSeqInst::NoeudSeqInst() : tabInst() {
}

////////////////////////////////////////////////////////////////////////////////

int NoeudSeqInst::getValeur() {
    int valeur = 0;
    for (unsigned int i = 0; i < tabInst.size(); i++)
        valeur = tabInst[i]->getValeur(); // on evalue chaque instruction de la séquence
    return valeur; // par convention, resultat = valeur de la derniere instruction
}

////////////////////////////////////////////////////////////////////////////////

void NoeudSeqInst::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Sequence de " << tabInst.size() << " instruction(s)" << endl;
    for (unsigned int i = 0; i < tabInst.size(); i++)
        tabInst[i]->afficher(indentation + 1); // on affiche les fils en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////

void NoeudSeqInst::ajouteInstruction(Noeud* instruction) {
    tabInst.push_back(instruction);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstSi
////////////////////////////////////////////////////////////////////////////////

NoeudInstSi::NoeudInstSi(Noeud* c, Noeud* i) {
    this->setCondition(c);
    this->setInstructions(i);
    this->setSinon(NULL);
}

int NoeudInstSi::getValeur() {
    int r = 0;
    if (this->condition->getValeur() != 0) {
        r = this->instructions->getValeur();
    } else if(this->sinon != NULL) {
        r = this->sinon->getValeur();
    }
    return r;
}

void NoeudInstSi::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Si" << endl;
    Noeud::afficher(indentation);
    cout << "Condition : ";
    this->condition->afficher(indentation+1);
    Noeud::afficher(indentation);
    cout << "Instructions :";
    this->instructions->afficher(indentation+1);
    Noeud::afficher(indentation);
    if(this->sinon != NULL) {
        cout << "Sinon ";
        this->sinon->afficher(indentation);
    }
    cout << endl;
}

void NoeudInstSi::setCondition(Noeud* condition) {
    this->condition = condition;
}

void NoeudInstSi::setInstructions(Noeud* instructions) {
    this->instructions = instructions;
}

void NoeudInstSi::setSinon(Noeud* sinon) {
    this->sinon = sinon;
}

Noeud* NoeudInstSi::getSinon() {
    return this->sinon;
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstTantQue
////////////////////////////////////////////////////////////////////////////////

NoeudInstTantQue::NoeudInstTantQue(Noeud* c, Noeud* i) {
    this->setCondition(c);
    this->setInstructions(i);
}

int NoeudInstTantQue::getValeur() {
    int r = 0;
    while (this->condition->getValeur() != 0) {
        r = this->instructions->getValeur();
    }
    return r;
}

void NoeudInstTantQue::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Tant que" << endl << "Condition : ";
    this->condition->afficher(indentation+1);
    Noeud::afficher(indentation);
    cout << "Instructions :";
    this->instructions->afficher(indentation+1);
    cout << endl;
}

void NoeudInstTantQue::setCondition(Noeud* condition) {
    this->condition = condition;
}

void NoeudInstTantQue::setInstructions(Noeud* instructions) {
    this->instructions = instructions;
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstRepeter
////////////////////////////////////////////////////////////////////////////////

NoeudInstRepeter::NoeudInstRepeter(Noeud* c, Noeud* i) {
    this->setCondition(c);
    this->setInstructions(i);
}

int NoeudInstRepeter::getValeur() {
    int r;
    r = this->instructions->getValeur();
    while (this->condition->getValeur() != 0) {
        r = this->instructions->getValeur();
    }
    return r;
}

void NoeudInstRepeter::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Tant que" << endl;
    Noeud::afficher(indentation);
    cout << "Condition : " << endl;
    this->condition->afficher(indentation+1);
    Noeud::afficher(indentation);
    cout << "Instructions :" << endl;
    this->instructions->afficher(indentation+1);
    cout << endl;
}

void NoeudInstRepeter::setCondition(Noeud* condition) {
    this->condition = condition;
}

void NoeudInstRepeter::setInstructions(Noeud* instructions) {
    this->instructions = instructions;
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstPour
////////////////////////////////////////////////////////////////////////////////

NoeudInstPour::NoeudInstPour(Noeud* i, Noeud* c, Noeud* a, Noeud* is) {
    this->setInit(i);
    this->setCondition(c);
    this->setAffectation(a);
    this->setInstructions(is);
}

int NoeudInstPour::getValeur() {
    int r = 0;
    this->init->getValeur();
    while (this->condition->getValeur() != 0) {
        r = this->instructions->getValeur();
        this->affectation->getValeur();
    }
    return r;
}

void NoeudInstPour::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Pour" << endl;
    Noeud::afficher(indentation);
    cout << "Init : " << endl;
    this->init->afficher(indentation+1);
    Noeud::afficher(indentation);
    cout << "Jusqu'à : " << endl;
    this->condition->afficher(indentation+1);
    Noeud::afficher(indentation);
    cout << "Itération : " << endl;
    this->affectation->afficher(indentation+1);
    Noeud::afficher(indentation);
    cout << "Instructions :" << endl;
    this->instructions->afficher(indentation+1);
    cout << endl;
}

void NoeudInstPour::setInit(Noeud* init) {
    this->init = init;
}

void NoeudInstPour::setCondition(Noeud* condition) {
    this->condition = condition;
}

void NoeudInstPour::setAffectation(Noeud* affectation) {
    this->affectation = affectation;
}

void NoeudInstPour::setInstructions(Noeud* instructions) {
    this->instructions = instructions;
}

////////////////////////////////////////////////////////////////////////////////
// NoeudLire
////////////////////////////////////////////////////////////////////////////////

NoeudLire::NoeudLire(Noeud* var) {
    this->var = var;
}

int NoeudLire::getValeur() {
    int l;
    cin >> l;
    ((SymboleValue*) this->var)->setValeur(l);
    return l;
}

void NoeudLire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Lire" << endl;
    this->var->afficher();
    cout << endl;
}

////////////////////////////////////////////////////////////////////////////////
// NoeudEcrire
////////////////////////////////////////////////////////////////////////////////

NoeudEcrire::NoeudEcrire(Noeud* val) {
    this->val = val;
}

int NoeudEcrire::getValeur() {
    val->afficher();
    return 0;
}

void NoeudEcrire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Lire" << endl;
    this->val->afficher();
    cout << endl;
}

////////////////////////////////////////////////////////////////////////////////
// NoeudAffectation
////////////////////////////////////////////////////////////////////////////////

NoeudAffectation::NoeudAffectation(Noeud* variable, Noeud* expression) {
    this->variable = variable;
    this->expression = expression;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudAffectation::getValeur() {
    int valeur = expression->getValeur(); // on évalue l'expression
    ((SymboleValue*) variable)->setValeur(valeur); // on affecte la variable
    return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////

void NoeudAffectation::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Affectation" << endl;
    variable->afficher(indentation + 1); // on affiche variable et expression
    expression->afficher(indentation + 1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurBooleen
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurBooleen::NoeudOperateurBooleen(Symbole operateur,
        Noeud* operandeGauche,
        Noeud* operandeDroit) {
    this->operateur = operateur;
    this->operandeGauche = operandeGauche;
    this->operandeDroit = operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudOperateurBooleen::getValeur() {
    int valeur = 0;
    int og = operandeGauche->getValeur();
    int od = operandeDroit->getValeur();
    if (this->operateur == "et" && og != 0 && od != 0) valeur = 1;
    else if (this->operateur == "ou" && (og != 0 || od != 0)) valeur = 1;
    return valeur;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudOperateurBooleen::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Operateur Booléen \"" << this->operateur.getChaine() << "\" applique a : " << endl;
    operandeGauche->afficher(indentation + 1); // on affiche fils gauche et fils droit
    operandeDroit->afficher(indentation + 1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////

Noeud* NoeudOperateurBooleen::getOperandeDroit() {
    return this->operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudOperateurBooleen::setOperandeDroit(Noeud* operande) {
    this->operandeDroit = operande;
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurUnaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurUnaire::NoeudOperateurUnaire(Symbole operateur, Noeud* operande) {
    this->operateur = operateur;
    this->operande = operande;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudOperateurUnaire::getValeur() {
    int valeur = 0;
    int o = operande->getValeur();
    if (this->operateur == "-") valeur = -valeur;
    else if (this->operateur == "non" && o == 0) valeur = 1;
    return valeur;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudOperateurUnaire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Operateur Unaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
    operande->afficher(indentation + 1);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurBinaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurBinaire::NoeudOperateurBinaire(Symbole operateur,
        Noeud* operandeGauche,
        Noeud* operandeDroit) {
    this->operateur = operateur;
    this->operandeGauche = operandeGauche;
    this->operandeDroit = operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudOperateurBinaire::getValeur() {
    int valeur = 0;
    int og = operandeGauche->getValeur();
    int od = operandeDroit->getValeur();
    if (this->operateur == "+") valeur = og + od;
    else if (this->operateur == "-") valeur = og - od;
    else if (this->operateur == "*") valeur = og * od;
    else // this->operateur=="/"
        if (od != 0)
        valeur = og / od;
    else {
        cout << "Erreur pendant l'interpretation : division par zero" << endl;
        exit(0); // plus tard on levera une exception
    }
    return valeur;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudOperateurBinaire::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
    operandeGauche->afficher(indentation + 1); // on affiche fils gauche et fils droit
    operandeDroit->afficher(indentation + 1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurBinaire
////////////////////////////////////////////////////////////////////////////////

NoeudRelation::NoeudRelation(Symbole operateur,
        Noeud* operandeGauche,
        Noeud* operandeDroit) {
    this->operateur = operateur;
    this->operandeGauche = operandeGauche;
    this->operandeDroit = operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////

int NoeudRelation::getValeur() {
    int valeur = 0;
    int og = operandeGauche->getValeur();
    int od = operandeDroit->getValeur();
    if(this->operateur == "==") {
        if(og == od) valeur = 1;
        else valeur = 0;
    }
    else if(this->operateur == "!=") {
        if(og != od) valeur = 1;
        else valeur = 0;
    }
    else if(this->operateur == "<=") {
        if(og <= od) valeur = 1;
        else valeur = 0;
    }
    else if(this->operateur == ">=") {
        if(og >= od) valeur = 1;
        else valeur = 0;
    }
    else if(this->operateur == "<") {
        if(og < od) valeur = 1;
        else valeur = 0;
    }
    else if(this->operateur == ">") {
        if(og > od) valeur = 1;
        else valeur = 0;
    }
    return valeur;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudRelation::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Relation \"" << this->operateur.getChaine() << "\" applique a : " << endl;
    operandeGauche->afficher(indentation + 1); // on affiche fils gauche et fils droit
    operandeDroit->afficher(indentation + 1); // en augmentant l'indentation
}